/*****************************************************************************/
/*                                                                           */
/*    Domino Operation System Driver Module                                  */
/*                                                                           */
/*    Copyright (C) 2008 Laszlo Arvai                                        */
/*                                                                           */
/*    ------------------------------------------------------------------     */
/*    Module: drvUART - Win32 UART Driver                                    */
/*                                                                           */
/*    IOconfig declarations:                                                 */
/*      UART1_BAUD_RATE = Desired baud UART1 [baud]                          */
/*      UART2_BAUD_RATE = Desired baud rate for UART2 [baud]                 */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Includes
#include <windows.h>
#include <tchar.h>
#include <drvUART.h>
#include "sysSettings.h"
#include "drvIOConfig.h"

///////////////////////////////////////////////////////////////////////////////
// Defines
#ifdef UART_BAUD_RATE
#define UART1_BAUD_RATE UART_BAUD_RATE
#define UART2_BAUD_RATE UART_BAUD_RATE
#endif

///////////////////////////////////////////////////////////////////////////////
// Types

///////////////////////////////////////////////////////////////////////////////
// Module global variables

// UART1 variable
HANDLE l_uart1_handle = NULL;
HANDLE l_uart1_thread = NULL;
HANDLE l_uart1_thread_exit_event = NULL;
HANDLE l_uart1_comm_event = NULL;
HANDLE l_uart1_tx_enabled_event = NULL;
OVERLAPPED l_uart1_overlapped;

BOOL g_cleanup_installed = FALSE;

///////////////////////////////////////////////////////////////////////////////
// Function prototypes
static DWORD WINAPI UART1_Thread(LPVOID lpParameter);
static void UART_Cleanup(void);

/*****************************************************************************/
/* General UART Functions                                                          */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Cleanup function
static void UART_Cleanup(void)
{
  HANDLE threads[2];
  int i=0;

  // create thread list
  if( l_uart1_thread != NULL )
    threads[i++] = l_uart1_thread;

  // stop threads
  SetEvent(l_uart1_thread_exit_event);
  WaitForMultipleObjects(i, threads, TRUE, 1000);
  
  // release resources
  CloseHandle(l_uart1_thread);
  CloseHandle(l_uart1_thread_exit_event);
  CloseHandle(l_uart1_comm_event);
  CloseHandle(l_uart1_tx_enabled_event);

  // close UART
  if( l_uart1_handle != NULL )
    CloseHandle(l_uart1_handle);
}

/*****************************************************************************/
/* UART 1 Functions                                                          */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Initialize UART driver
void drvUART1Init(void)
{
  BOOL success = TRUE;
  DCB comm_state;
  COMMTIMEOUTS time;

  l_uart1_handle = CreateFile(  UART1_NAME,
                                GENERIC_READ | GENERIC_WRITE,
                                0,
                                0,
                                OPEN_EXISTING,
                                FILE_FLAG_OVERLAPPED,
                                0);

  if( l_uart1_handle == INVALID_HANDLE_VALUE )
    success = FALSE;

  // setup port
  if( success )
    success = GetCommState( l_uart1_handle, &comm_state );

  comm_state.BaudRate = UART1_BAUD_RATE;
  comm_state.ByteSize = 8;
  comm_state.Parity   = NOPARITY;
  comm_state.StopBits = ONESTOPBIT;

  if( success )
    success = SetCommState( l_uart1_handle, &comm_state );
                 
  // set timeouts
  if( success )
    success = GetCommTimeouts( l_uart1_handle, &time );

  time.ReadIntervalTimeout          = MAXDWORD;
  time.ReadTotalTimeoutMultiplier   = 0;
  time.ReadTotalTimeoutConstant     = 0;
  time.WriteTotalTimeoutConstant    = 0;
  time.WriteTotalTimeoutMultiplier  = 0;

  if( success )
    success = SetCommTimeouts( l_uart1_handle, &time);
                   
  // setup queue
  if( success )
    success = SetupComm( l_uart1_handle, 1024, 1024 );

  // set event mask
  SetCommMask( l_uart1_handle, EV_RXCHAR );

  // create UART1 thread
  l_uart1_thread = CreateThread( NULL,
                                  0,
                                  (LPTHREAD_START_ROUTINE)UART1_Thread,
                                  NULL,
                                  0,
                                  NULL);
  if(l_uart1_thread  == NULL)
    success = FALSE;

  // create thread running event
  l_uart1_thread_exit_event = CreateEvent( NULL, TRUE, FALSE, NULL);

  // comminication event
  l_uart1_comm_event = CreateEvent( NULL, FALSE, FALSE, NULL);

  // TX start event
  l_uart1_tx_enabled_event = CreateEvent( NULL, TRUE, FALSE, NULL);
  
  // create overlapped struct
  memset(&l_uart1_overlapped, 0, sizeof(l_uart1_overlapped));
  l_uart1_overlapped.hEvent = l_uart1_comm_event;
  
  // install exit handler
  if( !g_cleanup_installed )
  {
    atexit(UART_Cleanup);
    g_cleanup_installed = TRUE;
  }
}

///////////////////////////////////////////////////////////////////////////////
// Send byte on UART
void drvUART1SendCharacter( dosChar in_byte )
{
  DWORD bytes_written;

  WriteFile( l_uart1_handle, &in_byte, sizeof(in_byte), &bytes_written, &l_uart1_overlapped);
}

///////////////////////////////////////////////////////////////////////////////
// Enable transmit interrupt
void drvUART1EnableTransmitInterrupt(void)
{
  SetEvent(l_uart1_tx_enabled_event);
}

///////////////////////////////////////////////////////////////////////////////
// Disable transmit itnerrupt
void drvUART1DisableTransmitInterrupt(void)
{
  ResetEvent(l_uart1_tx_enabled_event);
}

///////////////////////////////////////////////////////////////////////////////
// UART1 Thread function
static DWORD WINAPI UART1_Thread(LPVOID lpParameter)
{
  HANDLE handles[3];
  DWORD dwEventMask = 0;
  DWORD wait_res;
  DWORD comm_mask;
  dosByte buffer;
  DWORD bytes_read;

  // store handles
  handles[0] = l_uart1_thread_exit_event;
  handles[1] = l_uart1_comm_event;
  handles[2] = l_uart1_tx_enabled_event;

  // set running event
  ResetEvent( l_uart1_thread_exit_event );

  while( WaitForSingleObject(l_uart1_thread_exit_event, 0) != WAIT_OBJECT_0 )
  {                         
    // start overlapped comm event watching
    WaitCommEvent( l_uart1_handle, &dwEventMask, &l_uart1_overlapped) ;

    // wait for eighter thread stop or communication event
    wait_res = WaitForMultipleObjects(3, handles, FALSE, INFINITE);

		// check for communication event
		if( WAIT_OBJECT_0 + 1 )
		{
			if( GetCommMask(l_uart1_handle, &comm_mask) )
      {
				// character received event
        if( (comm_mask & EV_RXCHAR) != 0 )
        {
					// process received characters
          do
          {
						// read pending character
            ReadFile( l_uart1_handle, &buffer, sizeof(buffer), &bytes_read, &l_uart1_overlapped);

            // process character
            if( bytes_read > 0 )
							krnlUART1ReceiveInterrupt(buffer);
          } while( bytes_read > 0 );
        }
      }
    }

		// check for TX enabled
		if( WaitForSingleObject(l_uart1_tx_enabled_event, 0) == WAIT_OBJECT_0 )
		{
			krnlUART1TransmitInterrupt();
		}

		// Yield to other task
    Sleep(0);
  }

  // exit thread
  ExitThread( 0 );
}

#if 0
/*****************************************************************************/
/* UART 2 Functions                                                          */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Initialize UART driver
void drvUART2Init(void)
{
	// open uart
	OpenUART2(UART_EN | UART_NO_PAR_8BIT | UART_1STOPBIT, // Module is ON
			      UART_TX_ENABLE | UART_INT_TX | UART_RX_ENABLE | UART_INT_RX_CHAR,		        // Enable TX & RX
			      sysPBLCK_FREQUENCY/16/UART2_BAUD_RATE-1);    // Set baudrate

	// Configure uart interrupt
	ConfigIntUART2( UART_RX_INT_EN | UART_TX_INT_EN | UART_ERR_INT_DIS | UART_INT_PR2 );
}

///////////////////////////////////////////////////////////////////////////////
// Send byte on UART
void drvUART2SendCharacter( dosChar in_byte )
{
	U2TXREG = in_byte;
}

///////////////////////////////////////////////////////////////////////////////
// Enable transmit interrupt
void drvUART2EnableTransmitInterrupt(void)
{
  if( U2STAbits.UTXBF == 0 )
		krnlUART2TransmitInterrupt();
	else
		l_uart2_character_pending = dosTrue;
}

///////////////////////////////////////////////////////////////////////////////
// Disable transmit itnerrupt
void drvUART2DisableTransmitInterrupt(void)
{
  l_uart2_character_pending = dosFalse;
}

///////////////////////////////////////////////////////////////////////////////
// UART 2 interrupt handler
void __ISR(_UART2_VECTOR, ipl2) IntUart2Handler(void)
{
	// Handle RX interrupt
	if(mU2RXGetIntFlag())
	{
		// Clear the RX interrupt Flag
	  mU2RXClearIntFlag();

		// Process received data
		krnlUART2ReceiveInterrupt(ReadUART2());
	}

	// Handle TX interrupt
	if( mU2TXGetIntFlag() )
	{
		mU2TXClearIntFlag();
		
		if( l_uart2_character_pending )
	  	krnlUART2TransmitInterrupt();
	}
}
#endif
